package network

import (
	"context"
	"net"
	"sync/atomic"
	"time"

	"gitee.com/doraemon1996/bunny/structure"
	"gitee.com/doraemon1996/bunny/timer"
	"github.com/gorilla/websocket"
)

// 网络连接对象
type Conn interface {
	ID() uint64           // 获取连接唯一ID
	Listener() Listener   // 获取连接所属监听器
	RawConn() net.Conn    // 获取底层连接
	Type() ConnectionType // 获取连接类型
	String() string       // 连接主要描述信息
	Status() StatusType   // 获取连接状态

	Init(context.Context)
	Start(EventHandler)
	Stop(Error)

	ReadBytes() uint64              // 获取总读字节计数
	ReadPackages() uint64           // 获取总读包计数
	WriteBytes() uint64             // 获取总写字节计数
	WritePackages() uint64          // 获取总写包计数
	SetReadLimit(int)               // 设置每次读Socket数据大小上限 开启连接前生效 对于UDP连接,该参数将限制消息包的大小上限
	SetWriteLimit(int)              // 设置每次写Socket数据大小上限 开启连接前生效
	SetCrontabPeriod(time.Duration) // 设置定时任务间隔 单位:毫秒 开启连接前生效
	RemoveCrontab()

	Send(any) error // 发送消息
}

// websocket网络连接对象
type ConnWS interface {
	Conn

	RawWSConn() *websocket.Conn             // 获取底层websocket连接
	WriteClose(code int, data string) Error // 写关闭连接消息
}

// 网络连接基类
type conn struct {
	ctl structure.Control

	// 创建Connection时赋值的字段
	id uint64   // 连接唯一ID
	ln Listener // 所属监听器

	// 初始化Connection时赋值的字段
	readBytes     uint64        // 总读字节计数
	readPackages  uint64        // 总读包计数
	writeBytes    uint64        // 总写字节计数
	writePackages uint64        // 总写包计数
	readLimit     int           // 每次读Socket数据大小上限
	writeLimit    int           // 每次写Socket数据大小上限
	crontabID     uint64        // 定时任务唯一ID 不为0时表示有定时任务
	crontabPeriod time.Duration // 定时任务间隔事件 单位毫秒
	sendChan      chan any      // 发送消息通道

	// 启动Connection时赋值的字段
	eventHandler EventHandler // 事件处理器
}

// 获取连接唯一ID
func (base *conn) ID() uint64 {
	return base.id
}

// 获取连接所属监听器
func (base *conn) Listener() Listener {
	return base.ln
}

// 初始化连接数据
func (base *conn) Init(parent context.Context) {
	base.ctl.Init(parent, func() {
		base.readBytes = 0
		base.readPackages = 0
		base.writeBytes = 0
		base.writePackages = 0
		base.readLimit = DefaultReadLimit
		base.writeLimit = DefaultWriteLimit
		base.crontabID = 0
		base.crontabPeriod = 0
		base.sendChan = make(chan any, SendChanCap)
	})
}

// 获取连接状态
func (base *conn) Status() StatusType {
	switch base.ctl.Status() {
	case structure.ControlStatus_Uninitialized:
		return Status_Uninit
	case structure.ControlStatus_Initialized:
		return Status_Init
	case structure.ControlStatus_Opening:
		return Status_Opening
	default:
		return Status_Closed
	}
}

// 获取总读字节计数
func (base *conn) ReadBytes() uint64 {
	return atomic.LoadUint64(&base.readBytes)
}

// 获取总读包计数
func (base *conn) ReadPackages() uint64 {
	return atomic.LoadUint64(&base.readPackages)
}

// 获取总写字节计数
func (base *conn) WriteBytes() uint64 {
	return atomic.LoadUint64(&base.writeBytes)
}

// 获取总写包计数
func (base *conn) WritePackages() uint64 {
	return atomic.LoadUint64(&base.writePackages)
}

// 设置每次读Socket数据大小上限
// 仅在调用 Connection.Start() 前设置才有效
// 对于UDP连接,该参数将限制消息包的大小上限
func (base *conn) SetReadLimit(limit int) {
	if !base.ctl.Initialized() && limit > 0 {
		base.readLimit = limit
	}
}

// 设置每次写Socket数据大小上限
// 仅在调用 Connection.Start() 前设置才有效
// 对于UDP连接,该参数将限制消息包的大小上限
func (base *conn) SetWriteLimit(limit int) {
	if !base.ctl.Initialized() && limit > 0 {
		base.writeLimit = limit
	}
}

// 设置定时任务间隔 单位:毫秒
// 仅在调用 Connection.Start() 前设置才有效
func (base *conn) SetCrontabPeriod(period time.Duration) {
	if !base.ctl.Initialized() && period.Milliseconds() >= 1 {
		base.crontabPeriod = period
	}
}

// 移除定时任务
func (base *conn) RemoveCrontab() {
	if base.ctl.Opening() && base.crontabID != 0 {
		timer.Remove(base.crontabID)
		base.crontabID = 0
	}
}
